--------------------------------------------------------------------------------
--      Copyright (c) 2015 - 2016 , 蒙占志(topameng) topameng@gmail.com
--      All rights reserved.
--      Use, modification and distribution are subject to the "MIT License"
--------------------------------------------------------------------------------
-- added by wsh @ 2017-12-28
-- 注意：
-- 1、已经被修改，别从tolua轻易替换来做升级

local rawget       = rawget
local setmetatable = setmetatable
local type         = type
local Vector3      = Vector3
local zero         = Vector3.zero

local Bounds       = {
    center  = Vector3.zero,
    extents = Vector3.zero,
}

local _getter      = {}
local unity_bounds = CS.UnityEngine.Bounds

Bounds.__index     = function(t, k)
    local var = rawget(Bounds, k)
    if var ~= nil then
        return var
    end

    var = rawget(_getter, k)
    if var ~= nil then
        return var(t)
    end

    return rawget(unity_bounds, k)
end

Bounds.__call      = function(t, center, size)
    return setmetatable({ center = center, extents = size * 0.5 }, Bounds)
end

function Bounds.New(center, size)
    return setmetatable({ center = center, extents = size * 0.5 }, Bounds)
end

function Bounds:Get()
    local size = self:GetSize()
    return self.center, size
end

function Bounds:GetSize()
    return self.extents * 2
end

function Bounds:SetSize(value)
    self.extents = value * 0.5
end

function Bounds:GetMin()
    return self.center - self.extents
end

function Bounds:SetMin(value)
    self:SetMinMax(value, self:GetMax())
end

function Bounds:GetMax()
    return self.center + self.extents
end

function Bounds:SetMax(value)
    self:SetMinMax(self:GetMin(), value)
end

function Bounds:SetMinMax(min, max)
    self.extents = (max - min) * 0.5
    self.center  = min + self.extents
end

function Bounds:Encapsulate(point)
    self:SetMinMax(Vector3.Min(self:GetMin(), point), Vector3.Max(self:GetMax(), point))
end

function Bounds:Expand(amount)
    if type(amount) == "number" then
        amount = amount * 0.5
        self.extents:Add(Vector3.New(amount, amount, amount))
    else
        self.extents:Add(amount * 0.5)
    end
end

function Bounds:Intersects(bounds)
    local min  = self:GetMin()
    local max  = self:GetMax()

    local min2 = bounds:GetMin()
    local max2 = bounds:GetMax()

    return min.x <= max2.x and max.x >= min2.x and min.y <= max2.y and max.y >= min2.y and min.z <= max2.z and max.z >= min2.z
end

function Bounds:Contains(p)
    local min = self:GetMin()
    local max = self:GetMax()

    if p.x < min.x or p.y < min.y or p.z < min.z or p.x > max.x or p.y > max.y or p.z > max.z then
        return false
    end

    return true
end

function Bounds:IntersectRay(ray)
    local tmin   = -Mathf.Infinity
    local tmax   = Mathf.Infinity

    local t0, t1, f
    local t      = self:GetCenter() - ray:GetOrigin()
    local p      = { t.x, t.y, t.z }
    t            = self.extents
    local extent = { t.x, t.y, t.z }
    t            = ray:GetDirection()
    local dir    = { t.x, t.y, t.z }

    for i = 1, 3 do
        f  = 1 / dir[i]
        t0 = (p[i] + extent[i]) * f
        t1 = (p[i] - extent[i]) * f

        if t0 < t1 then
            if t0 > tmin then
                tmin = t0
            end
            if t1 < tmax then
                tmax = t1
            end
            if tmin > tmax then
                return false
            end
            if tmax < 0 then
                return false
            end
        else
            if t1 > tmin then
                tmin = t1
            end
            if t0 < tmax then
                tmax = t0
            end
            if tmin > tmax then
                return false
            end
            if tmax < 0 then
                return false
            end
        end
    end

    return true, tmin
end

function Bounds:ClosestPoint(point)
    local t        = point - self:GetCenter()
    local closest  = { t.x, t.y, t.z }
    local et       = self.extents
    local extent   = { et.x, et.y, et.z }
    local distance = 0
    local delta

    for i = 1, 3 do
        if closest[i] < -extent[i] then
            delta      = closest[i] + extent[i]
            distance   = distance + delta * delta
            closest[i] = -extent[i]
        elseif closest[i] > extent[i] then
            delta      = closest[i] - extent[i]
            distance   = distance + delta * delta
            closest[i] = extent[i]
        end
    end

    if distance == 0 then
        return rkPoint, 0
    else
        outPoint = closest + self:GetCenter()
        return outPoint, distance
    end
end

function Bounds:Destroy()
    self.center = nil
    self.size   = nil
end

Bounds.__tostring     = function(self)
    return string.format("Center: %s, Extents %s", tostring(self.center), tostring(self.extents))
end

Bounds.__eq           = function(a, b)
    return a.center == b.center and a.extents == b.extents
end

_getter.size          = Bounds.GetSize
_getter.min           = Bounds.GetMin
_getter.max           = Bounds.GetMax

Bounds.unity_bounds   = CS.UnityEngine.Bounds
CS.UnityEngine.Bounds = Bounds
setmetatable(Bounds, Bounds)
return Bounds
